/*
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections.Generic;

using Gtk;
using Glade;
using Mono.Unix;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Objects;
using DCSharp.Backend.Managers;

namespace DCSharp.Gui
{
	public class UploadPage : Page
	{
		private UploadManager uploadManager;

		private UploadStore uploadStore;
		private UploadView uploadView;

		private List<Uploader> activeUploads;
		private uint timeout;

		private ActionGroup actionGroup;
		private Action stopAction;
		private Action removeAction;
		private Action clearAction;

		[Widget]
		private ScrolledWindow scrolledWindow;

		#region Constructors

		public UploadPage(UploadManager uploadManager) : base("UploadPage.glade")
		{
			if (uploadManager == null)
			{
				throw new ArgumentNullException("uploadManager");
			}
			this.uploadManager = uploadManager;

			uploadStore = new UploadStore();
			uploadStore.SetSortColumnId((int)UploadStore.Column.Started,
				SortType.Descending);

			activeUploads = new List<Uploader>();

			// User interface
			ActionEntry[] entries = new ActionEntry[] {
				new ActionEntry("Stop", Stock.MediaStop, Catalog.GetString("S_top"),
					null, Catalog.GetString("Stops the selected uploads"),
					OnStopActivated),
				new ActionEntry("Remove", Stock.Remove, null,
					null, Catalog.GetString("Stops and removes the selected uploads"),
					OnRemoveActivated),
				new ActionEntry("Clear", Stock.Clear, Catalog.GetString("_Clean Up"),
					null, Catalog.GetString("Removes the finished or stopped uploads"),
					OnClearActivated),

				new ActionEntry("VisibleColumns", null, Catalog.GetString("_Visible Columns..."), null,
					Catalog.GetString("Edit visible columns"),
					null),
			};

			actionGroup = new ActionGroup("UploadPageActions");
			actionGroup.Add(entries);

			stopAction = actionGroup["Stop"];
			removeAction = actionGroup["Remove"];
			clearAction = actionGroup["Clear"];

			stopAction.Sensitive = removeAction.Sensitive =
				clearAction.Sensitive = false;

			// Upload view
			uploadView = new UploadView(uploadStore);
			uploadView.KeyPressEvent += OnViewKeyPressEvent;
			uploadView.Selection.Changed += OnViewSelectionChanged;
			uploadView.Show();

			uploadView.GetColumn("Directory").Visible = false;
			uploadView.GetColumn("Size").Visible = false;

			scrolledWindow.Add(uploadView);

			// Visible columns
			VisibleColumnsWindow.ConnectToWidget(this, actionGroup["VisibleColumns"],
				uploadView.Columns, uploadView.Columns[0], uploadView.Columns[1]);

			// Initialize
			lock (uploadManager.SyncRoot)
			{
				foreach (Uploader uploader in uploadManager)
				{
					Add(uploader);
				}
				uploadManager.UploadStarted += OnUploadStarted;
			}
		}

		#endregion

		#region Properties

		public override string Title
		{
			get { return Catalog.GetString("Uploads"); }
		}

		public override Gdk.Pixbuf Icon
		{
			get { return RenderIcon(Stock.GoUp, IconSize.Menu, null); }
		}

		public override string Tooltip
		{
			get { return Title; }
		}

		public override string Status
		{
			get { return null; }
		}

		public override ActionGroup Actions
		{
			get { return actionGroup; }
		}

		public int VisibleColumns
		{
			get { return Util.GetVisibleColumns(uploadView.Columns); }
			set { Util.SetVisibleColumns(uploadView.Columns, value | 3); }
		}

		#endregion

		#region Methods

		protected override void OnDestroyed()
		{
			uploadManager.UploadStarted -= OnUploadStarted;
			base.OnDestroyed();
		}

		private void Add(Uploader uploader)
		{
			if (!uploadStore.Contains(uploader) && uploader.Request != null &&
				uploader.Request.Type != RequestType.HashTree)
			{
				uploadStore.Add(uploader);
				uploader.ActiveChanged += OnActiveChanged;

				UpdateActive(uploader);
				UpdateSensitivity();

				OnPageChanged();
			}
		}

		private TreeIter Remove(Uploader uploader)
		{
			uploader.ActiveChanged -= OnActiveChanged;
			activeUploads.Remove(uploader);

			return uploadStore.Remove(uploader);
		}

		private void UpdateActive(Uploader uploader)
		{
			if (uploader.Active && !activeUploads.Contains(uploader))
			{
				activeUploads.Add(uploader);
				if (timeout == 0)
				{
					timeout = GLib.Timeout.Add(1000, OnRefreshTimeout);
				}
			}
			else if (!uploader.Active)
			{
				activeUploads.Remove(uploader);
			}
		}

		private void UpdateSensitivity()
		{
			UpdateAbortActionSensitivity();
			UpdateClearActionSensitivity();
		}

		private void UpdateAbortActionSensitivity()
		{
			Uploader[] selectedUploads = uploadView.GetSelectedUploads(false);
			removeAction.Sensitive = selectedUploads.Length > 0;

			foreach (Uploader uploader in selectedUploads)
			{
				if (uploader.Active)
				{
					stopAction.Sensitive = true;
					return;
				}
			}
			stopAction.Sensitive = false;
		}

		private void UpdateClearActionSensitivity()
		{
			TreeIter iter;
			if (uploadStore.GetIterFirst(out iter))
			{
				do
				{
					Uploader uploader = (Uploader)uploadStore.GetValue(
						iter, (int)UploadStore.Column.Object);

					if (!uploader.Active)
					{
						clearAction.Sensitive = true;
						return;
					}
				}
				while (uploadStore.IterNext(ref iter));
			}
			clearAction.Sensitive = false;
		}

		#region Event handlers

		private void OnViewKeyPressEvent(object obj, KeyPressEventArgs args)
		{
			if (args.Event.Key == Gdk.Key.Delete)
			{
				OnRemoveActivated(null, null);
			}
		}

		private void OnViewSelectionChanged(object obj, EventArgs args)
		{
			UpdateAbortActionSensitivity();
		}

		private void OnStopActivated(object obj, EventArgs args)
		{
			foreach (Uploader uploader in uploadView.GetSelectedUploads(false))
			{
				uploader.Stop();
			}
		}

		private void OnRemoveActivated(object obj, EventArgs args)
		{
			foreach (Uploader uploader in uploadView.GetSelectedUploads(false))
			{
				Remove(uploader);
				uploader.Stop();
			}
			UpdateSensitivity();
		}

		private void OnClearActivated(object obj, EventArgs args)
		{
			TreeIter iter;
			if (uploadStore.GetIterFirst(out iter))
			{
				do
				{
					Uploader uploader = (Uploader)uploadStore.GetValue(
						iter, (int)UploadStore.Column.Object);

					if (!uploader.Active)
					{
						iter = Remove(uploader);
						continue;
					}
					uploadStore.IterNext(ref iter);
				}
				while (uploadStore.IterIsValid(iter));
			}
			UpdateSensitivity();
		}

		private bool OnRefreshTimeout()
		{
			if (activeUploads.Count > 0)
			{
				foreach (Uploader uploader in activeUploads)
				{
					uploadStore.Update(uploader);
				}
				return true;
			}
			timeout = 0;
			return false;
		}

		#region External

		private void OnUploadStarted(object obj, UploadEventArgs args)
		{
			Application.Invoke(delegate
			{
				Add(args.Uploader);
			});
		}

		private void OnActiveChanged(object obj, EventArgs args)
		{
			Uploader uploader = (Uploader)obj;
			Application.Invoke(delegate
			{
				// Check Contains since the upload might have been removed after
				// this timeout handler was added
				if (uploadStore.Contains(uploader))
				{
					uploadStore.Update(uploader);
					UpdateActive(uploader);

					UpdateSensitivity();
					QueueDraw();

					OnPageChanged();
				}
			});
		}

		#endregion

		#endregion

		#endregion
	}
}
